﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Configuration;
using System.Net;
using System.Net.NetworkInformation;
using Common;


namespace FileSlurpeeCore
{
    public class FileSlurper
    {

        public List<string> SearchPaths { get; set; }
        public List<string> FilePatternList { get; set; }
        public string DestinationPath { get; set; }
        public bool Recursive { get; set; }
        public bool EnumerateNetworkShares { get; set; }
        public bool EnumerateLocalFolders { get; set; }
        public bool ProbeHostBeforeEnumeration { get; set; }
        public bool EnumerateSpecialShares { get; set; }
        public int MaxThreads { get; set; }
        public int BatchSize { get; set; }


        public void SlurpFiles()
        {
            if (EnumerateLocalFolders)
                SearchLocalFolders();
            if (EnumerateNetworkShares)
                SearchNetworkShares();
        }

        private void SearchNetworkShares()
        {
            var nics = NetworkInterface.GetAllNetworkInterfaces();
            foreach (var card in nics)
            {
                var ipInfo = card.GetIPProperties();
                var ipv4Addresses = ipInfo.UnicastAddresses.Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);

                ipv4Addresses.AsParallel().ForAll(addressInfo => 
                {
                    var addressesInNetwork = addressInfo.Address.GetAddressesInNetwork(addressInfo.IPv4Mask);

                    Parallel.ForEach(addressesInNetwork, a =>
                    {
                        if (a.Equals(addressInfo.Address))
                            return;

                        var shareCollection = ShareCollection.GetShares("\\\\" + a.ToString());

                        var shareList = new List<Share>();

                        foreach (Share share in shareCollection)
                        {
                            if (share.ShareType == ShareType.Disk || (EnumerateSpecialShares && share.ShareType == ShareType.Special))
                            {
                                shareList.Add(share);
                            }
                        }

                        foreach(var path in shareList)
                        //Parallel.ForEach(shareList, path =>
                        {
                            foreach(var pattern in FilePatternList)
                            //Parallel.ForEach(FilePatternList, pattern =>
                            {
                                var files = Directory.EnumerateFiles(path.ToString(), pattern, Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                                            .Take(BatchSize)
                                            .ToList();
                                            
                                Parallel.ForEach(files, file => {
                                    File.Copy(Path.Combine(path.ToString(), file), Path.Combine(DestinationPath, Path.GetFileName(file)));
                                });
                            }
                            //);
                        }
                        //);
                    });
                });
            }
        }

        private void SearchLocalFolders()
        {
            try
            {
                Parallel.ForEach(SearchPaths, path =>
                {
                    if (!Directory.Exists(path))
                        return;
   
                    FilePatternList.AsParallel().ForAll(pattern => 
                                                        Directory.EnumerateFiles(path, pattern, Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                                                                 .ToList()
                                                                 .AsParallel()
                                                                 .ForAll(file => File.Copy(Path.Combine(path, file), Path.Combine(DestinationPath, Path.GetFileName(file))))
                                                       );
                });

            }
            catch (Exception e)
            {

#if DEBUG //If not debug mode fail quietly
                throw e;
#endif
            }
        }
    }
}
